home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / nt / gd25s.zip / SDIFF.C < prev    next >
C/C++ Source or Header  |  1993-10-08  |  26KB  |  1,196 lines

  1. /* SDIFF -- interactive merge front end to diff
  2.    Copyright (C) 1992, 1993 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. GNU DIFF is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU DIFF; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* GNU SDIFF was written by Thomas Lord. */
  21.  
  22. #include "system.h"
  23. #include <stdio.h>
  24. #include <ctype.h>
  25. #include <signal.h>
  26. #include "getopt.h"
  27.  
  28. /* Size of chunks read from files which must be parsed into lines. */
  29. #define SDIFF_BUFSIZE ((size_t) 65536)
  30.  
  31. /* Default name of the diff program */
  32. #ifndef DIFF_PROGRAM
  33. #define DIFF_PROGRAM "/usr/bin/diff"
  34. #endif
  35.  
  36. /* Users' editor of nonchoice */
  37. #ifndef DEFAULT_EDITOR
  38. #define DEFAULT_EDITOR "ed"
  39. #endif
  40.  
  41. extern char version_string[];
  42. static char const *prog;
  43. static char const *diffbin = DIFF_PROGRAM;
  44. static char const *edbin = DEFAULT_EDITOR;
  45.  
  46. static char *tmpname;
  47. static int volatile tmpmade;
  48. static pid_t volatile diffpid;
  49.  
  50. struct line_filter;
  51.  
  52. static FILE *ck_fdopen PARAMS((int, char const *));
  53. static FILE *ck_fopen PARAMS((char const *, char const *));
  54. static RETSIGTYPE catchsig PARAMS((int));
  55. static VOID *xmalloc PARAMS((size_t));
  56. static char const *expand_name PARAMS((char *, int, char const *));
  57. static int edit PARAMS((struct line_filter *, int, struct line_filter *, int, FILE*));
  58. static int interact PARAMS((struct line_filter *, struct line_filter *, struct line_filter *, FILE*));
  59. static int lf_snarf PARAMS((struct line_filter *, char *, size_t));
  60. static int skip_white PARAMS((void));
  61. static size_t ck_fread PARAMS((char *, size_t, FILE *));
  62. static size_t lf_refill PARAMS((struct line_filter *));
  63. static void checksigs PARAMS((void));
  64. static void ck_fclose PARAMS((FILE *));
  65. static void ck_fflush PARAMS((FILE *));
  66. static void ck_fwrite PARAMS((char const *, size_t, FILE *));
  67. static void cleanup PARAMS((void));
  68. static void diffarg PARAMS((char const *));
  69. static void execdiff PARAMS((int, char const *, char const *, char const *));
  70. static void exiterr PARAMS((void));
  71. static void fatal PARAMS((char const *));
  72. static void flush_line PARAMS((void));
  73. static void give_help PARAMS((void));
  74. static void lf_copy PARAMS((struct line_filter *, int, FILE *));
  75. static void lf_init PARAMS((struct line_filter *, FILE *));
  76. static void lf_skip PARAMS((struct line_filter *, int));
  77. static void perror_fatal PARAMS((char const *));
  78. static void trapsigs PARAMS((void));
  79. static void untrapsig PARAMS((int));
  80. static void usage PARAMS((void));
  81.  
  82. /* this lossage until the gnu libc conquers the universe */
  83. #define PVT_tmpdir "/tmp"
  84. static char *private_tempnam PARAMS((char const *, char const *, int, size_t *));
  85. static int diraccess PARAMS((char const *));
  86. static int exists PARAMS((char const *));
  87.  
  88. /* Options: */
  89.  
  90. /* name of output file if -o spec'd */
  91. static char *out_file;
  92.  
  93. /* do not print common lines if true, set by -s option */
  94. static int suppress_common_flag;
  95.  
  96. static struct option const longopts[] =
  97. {
  98.   {"ignore-blank-lines", 0, 0, 'B'},
  99.   {"speed-large-files", 0, 0, 'H'},
  100.   {"ignore-matching-lines", 1, 0, 'I'},
  101.   {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
  102.   {"text", 0, 0, 'a'},
  103.   {"ignore-space-change", 0, 0, 'b'},
  104.   {"minimal", 0, 0, 'd'},
  105.   {"ignore-case", 0, 0, 'i'},
  106.   {"left-column", 0, 0, 'l'},
  107.   {"output", 1, 0, 'o'},
  108.   {"suppress-common-lines", 0, 0, 's'},
  109.   {"expand-tabs", 0, 0, 't'},
  110.   {"width", 1, 0, 'w'},
  111.   {"version", 0, 0, 'v'},
  112.   {0, 0, 0, 0}
  113. };
  114.  
  115. /* prints usage message and quits */
  116. static void
  117. usage ()
  118. {
  119.   fprintf (stderr, "Usage: %s [options] from-file to-file\n", prog);
  120.   fprintf (stderr, "Options:\n\
  121.        [-abBdHilstv] [-I regexp] [-o outfile] [-w columns]\n\
  122.        [--text] [--minimal] [--speed-large-files] [--expand-tabs]\n\
  123.        [--ignore-case] [--ignore-matching-lines=regexp]\n\
  124.        [--ignore-space-change] [--ignore-blank-lines] [--ignore-all-space]\n\
  125.        [--suppress-common-lines] [--left-column] [--output=outfile]\n\
  126.        [--version] [--width=columns]\n");
  127.   exit (2);
  128. }
  129.  
  130. static void
  131. cleanup ()
  132. {
  133.   if (0 < diffpid)
  134.     kill (diffpid, SIGPIPE);
  135.   if (tmpmade)
  136.     unlink (tmpname);
  137. }
  138.  
  139. static void
  140. exiterr ()
  141. {
  142.   cleanup ();
  143.   untrapsig (0);
  144.   checksigs ();
  145.   exit (2);
  146. }
  147.  
  148. static void
  149. fatal (msg)
  150.      char const *msg;
  151. {
  152.   fprintf (stderr, "%s: %s\n", prog, msg);
  153.   exiterr ();
  154. }
  155.  
  156. static void
  157. perror_fatal (msg)
  158.      char const *msg;
  159. {
  160.   int e = errno;
  161.   checksigs ();
  162.   fprintf (stderr, "%s: ", prog);
  163.   errno = e;
  164.   perror (msg);
  165.   exiterr ();
  166. }
  167.  
  168.  
  169. /* malloc freely or DIE! */
  170. static VOID *
  171. xmalloc (size)
  172.      size_t size;
  173. {
  174.   VOID *r = (VOID *) malloc (size);
  175.   if (!r)
  176.     fatal ("virtual memory exhausted");
  177.   return r;
  178. }
  179.  
  180. static FILE *
  181. ck_fopen (fname, type)
  182.      char const *fname, *type;
  183. {
  184.   FILE *r = fopen (fname, type);
  185.   if (!r)
  186.     perror_fatal (fname);
  187.   return r;
  188. }
  189.  
  190.  
  191. static FILE *
  192. ck_fdopen (fd, type)
  193.      int fd;
  194.      char const *type;
  195. {
  196.   FILE *r = fdopen (fd, type);
  197.   if (!r)
  198.     perror_fatal ("fdopen");
  199.   return r;
  200. }
  201.  
  202. static void
  203. ck_fclose (f)
  204.      FILE *f;
  205. {
  206.   if (fclose (f))
  207.     perror_fatal ("input/output error");
  208. }
  209.  
  210. static size_t
  211. ck_fread (buf, size, f)
  212.      char *buf;
  213.      size_t size;
  214.      FILE *f;
  215. {
  216.   size_t r = fread (buf, sizeof (char), size, f);
  217.   if (r == 0 && ferror (f))
  218.     perror_fatal ("input error");
  219.   return r;
  220. }
  221.  
  222. static void
  223. ck_fwrite (buf, size, f)
  224.      char const *buf;
  225.      size_t size;
  226.      FILE *f;
  227. {
  228.   if (fwrite (buf, sizeof (char), size, f) != size)
  229.     perror_fatal ("output error");
  230. }
  231.  
  232. static void
  233. ck_fflush (f)
  234.      FILE *f;
  235. {
  236.   if (fflush (f) != 0)
  237.     perror_fatal ("output error");
  238. }
  239.  
  240. #if !HAVE_MEMCHR
  241. char *
  242. memchr (s, c, n)
  243.      char const *s;
  244.      int c;
  245.      size_t n;
  246. {
  247.   unsigned char const *p = (unsigned char const *) s, *lim = p + n;
  248.   for (;  p < lim;  p++)
  249.     if (*p == c)
  250.       return (char *) p;
  251.   return 0;
  252. }
  253. #endif
  254.  
  255. #ifndef HAVE_WAITPID
  256. /* Emulate waitpid well enough for sdiff, which has at most two children.  */
  257. static pid_t
  258. waitpid (pid, stat_loc, options)
  259.      pid_t pid;
  260.      int *stat_loc;
  261.      int options;
  262. {
  263.   static int ostatus;
  264.   static pid_t opid;
  265.   int npid, status;
  266.  
  267.   if (pid == opid)
  268.     {
  269.       opid = 0;
  270.       status = ostatus;
  271.     }
  272.   else
  273.     while ((npid = wait (&status)) != pid)
  274.       {
  275.     if (npid < 0)
  276.       return npid;
  277.     opid = npid;
  278.     ostatus = status;
  279.       }
  280.   *stat_loc = status;
  281.   return pid;
  282. }
  283. #endif
  284.  
  285. static char const *
  286. expand_name (name, isdir, other_name)
  287.      char *name;
  288.      int isdir;
  289.      char const *other_name;
  290. {
  291.   if (strcmp (name, "-") == 0)
  292.     fatal ("cannot interactively merge standard input");
  293.   if (!isdir)
  294.     return name;
  295.   else
  296.     {
  297.       /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
  298.       char const
  299.     *p = strrchr (other_name, '/'),
  300.     *base = p ? p+1 : other_name;
  301.       size_t namelen = strlen (name), baselen = strlen (base);
  302.       char *r = xmalloc (namelen + baselen + 2);
  303.       memcpy (r, name, namelen);
  304.       r[namelen] = '/';
  305.       memcpy (r + namelen + 1, base, baselen + 1);
  306.       return r;
  307.     }
  308. }
  309.  
  310.  
  311.  
  312. struct line_filter {
  313.   FILE *infile;
  314.   char *bufpos;
  315.   char *buffer;
  316.   char *buflim;
  317. };
  318.  
  319. static void
  320. lf_init (lf, infile)
  321.      struct line_filter *lf;
  322.      FILE *infile;
  323. {
  324.   lf->infile = infile;
  325.   lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
  326.   lf->buflim[0] = '\n';
  327. }
  328.  
  329. /* Fill an exhausted line_filter buffer from its INFILE */
  330. static size_t
  331. lf_refill (lf)
  332.      struct line_filter *lf;
  333. {
  334.   size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
  335.   lf->bufpos = lf->buffer;
  336.   lf->buflim = lf->buffer + s;
  337.   lf->buflim[0] = '\n';
  338.   checksigs ();
  339.   return s;
  340. }
  341.  
  342. /* Advance LINES on LF's infile, copying lines to OUTFILE */
  343. static void
  344. lf_copy (lf, lines, outfile)
  345.      struct line_filter *lf;
  346.      int lines;
  347.      FILE *outfile;
  348. {
  349.   char *start = lf->bufpos;
  350.  
  351.   while (lines)
  352.     {
  353.       lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  354.       if (! lf->bufpos)
  355.     {
  356.       ck_fwrite (start, lf->buflim - start, outfile);
  357.       if (! lf_refill (lf))
  358.         return;
  359.       start = lf->bufpos;
  360.     }
  361.       else
  362.     {
  363.       --lines;
  364.       ++lf->bufpos;
  365.     }
  366.     }
  367.  
  368.   ck_fwrite (start, lf->bufpos - start, outfile);
  369. }
  370.  
  371. /* Advance LINES on LF's infile without doing output */
  372. static void
  373. lf_skip (lf, lines)
  374.      struct line_filter *lf;
  375.      int lines;
  376. {
  377.   while (lines)
  378.     {
  379.       lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  380.       if (! lf->bufpos)
  381.     {
  382.       if (! lf_refill (lf))
  383.         break;
  384.     }
  385.       else
  386.     {
  387.       --lines;
  388.       ++lf->bufpos;
  389.     }
  390.     }
  391. }
  392.  
  393. /* Snarf a line into a buffer.  Return EOF if EOF, 0 if error, 1 if OK.  */
  394. static int
  395. lf_snarf (lf, buffer, bufsize)
  396.      struct line_filter *lf;
  397.      char *buffer;
  398.      size_t bufsize;
  399. {
  400.   char *start = lf->bufpos;
  401.  
  402.   for (;;)
  403.     {
  404.       char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start);
  405.       size_t s = next - start;
  406.       if (bufsize <= s)
  407.     return 0;
  408.       memcpy (buffer, start, s);
  409.       if (next < lf->buflim)
  410.     {
  411.       buffer[s] = 0;
  412.       lf->bufpos = next + 1;
  413.       return 1;
  414.     }
  415.       if (! lf_refill (lf))
  416.     return s ? 0 : EOF;
  417.       buffer += s;
  418.       bufsize -= s;
  419.       start = next;
  420.     }
  421. }
  422.  
  423.  
  424.  
  425. int
  426. main (argc, argv)
  427.      int argc;
  428.      char *argv[];
  429. {
  430.   int opt;
  431.   int version_requested = 0;
  432.   char *editor = getenv ("EDITOR");
  433.   char *differ = getenv ("DIFF");
  434.  
  435.   prog = argv[0];
  436.   if (editor)
  437.     edbin = editor;
  438.   if (differ)
  439.     diffbin = differ;
  440.  
  441.   diffarg ("diff");
  442.  
  443.   /* parse command line args */
  444.   while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0))
  445.      != EOF)
  446.     {
  447.       switch (opt)
  448.     {
  449.     case 'a':
  450.       diffarg ("-a");
  451.       break;
  452.  
  453.     case 'b':
  454.       diffarg ("-b");
  455.       break;
  456.  
  457.     case 'B':
  458.       diffarg ("-B");
  459.       break;
  460.  
  461.     case 'd':
  462.       diffarg ("-d");
  463.       break;
  464.  
  465.     case 'H':
  466.       diffarg ("-H");
  467.       break;
  468.  
  469.     case 'i':
  470.       diffarg ("-i");
  471.       break;
  472.  
  473.     case 'I':
  474.       diffarg ("-I");
  475.       diffarg (optarg);
  476.       break;
  477.  
  478.     case 'l':
  479.       diffarg ("--left-column");
  480.       break;
  481.  
  482.     case 'o':
  483.       out_file = optarg;
  484.       break;
  485.  
  486.     case 's':
  487.       suppress_common_flag = 1;
  488.       break;
  489.  
  490.     case 't':
  491.       diffarg ("-t");
  492.       break;
  493.  
  494.     case 'v':
  495.       version_requested = 1;
  496.       fprintf (stderr, "GNU sdiff version %s\n", version_string);
  497.       ck_fflush (stderr);
  498.       break;
  499.  
  500.     case 'w':
  501.       diffarg ("-W");
  502.       diffarg (optarg);
  503.       break;
  504.  
  505.     case 'W':
  506.       diffarg ("-w");
  507.       break;
  508.  
  509.     default:
  510.       usage ();
  511.     }
  512.     }
  513.  
  514.   /* check: did user just want version message? if so exit. */
  515.   if (version_requested && argc - optind == 0)
  516.     exit (0);
  517.  
  518.   if (argc - optind != 2)
  519.     usage ();
  520.  
  521.   if (! out_file)
  522.     /* easy case: diff does everything for us */
  523.     execdiff (suppress_common_flag, "-y", argv[optind], argv[optind + 1]);
  524.   else
  525.     {
  526.       FILE *left, *right, *out, *diffout;
  527.       int diff_fds[2];
  528.       int interact_ok;
  529.       pid_t pid;
  530.       struct line_filter lfilt;
  531.       struct line_filter rfilt;
  532.       struct line_filter diff_filt;
  533.       int leftdir = diraccess (argv[optind]);
  534.       int rightdir = diraccess (argv[optind + 1]);
  535.  
  536.       if (leftdir && rightdir)
  537.     fatal ("both files to be compared are directories");
  538.  
  539.       left = ck_fopen (expand_name (argv[optind], leftdir, argv[optind + 1]), "r");
  540.       ;
  541.       right = ck_fopen (expand_name (argv[optind + 1], rightdir, argv[optind]), "r");
  542.       out = ck_fopen (out_file, "w");
  543.  
  544.       if (pipe (diff_fds))
  545.     perror_fatal ("pipe");
  546.  
  547.       trapsigs ();
  548.  
  549.       diffpid = pid = vfork ();
  550.  
  551.       if (pid == 0)
  552.     {
  553.       signal (SIGINT, SIG_IGN);  /* in case user interrupts editor */
  554.       signal (SIGPIPE, SIG_DFL);
  555.  
  556.       close (diff_fds[0]);
  557.       if (diff_fds[1] != STDOUT_FILENO)
  558.         {
  559.           dup2 (diff_fds[1], STDOUT_FILENO);
  560.           close (diff_fds[1]);
  561.         }
  562.  
  563.       execdiff (0, "--sdiff-merge-assist", argv[optind], argv[optind + 1]);
  564.     }
  565.  
  566.       if (pid < 0)
  567.     perror_fatal ("fork failed");
  568.  
  569.       close (diff_fds[1]);
  570.       diffout = ck_fdopen (diff_fds[0], "r");
  571.  
  572.       lf_init (&diff_filt, diffout);
  573.       lf_init (&lfilt, left);
  574.       lf_init (&rfilt, right);
  575.  
  576.       interact_ok = interact (&diff_filt, &lfilt, &rfilt, out);
  577.  
  578.       ck_fclose (diffout);
  579.       ck_fclose (left);
  580.       ck_fclose (right);
  581.       ck_fclose (out);
  582.  
  583.       {
  584.     int wstatus;
  585.  
  586.     while (waitpid (pid, &wstatus, 0) < 0)
  587.       if (errno == EINTR)
  588.         checksigs ();
  589.       else
  590.         perror_fatal ("wait failed");
  591.     diffpid = 0;
  592.  
  593.     if (tmpmade)
  594.       {
  595.         unlink (tmpname);
  596.         tmpmade = 0;
  597.       }
  598.  
  599.     if (! interact_ok)
  600.       exiterr ();
  601.  
  602.     if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 2))
  603.       fatal ("Subsidiary diff failed");
  604.  
  605.     untrapsig (0);
  606.     checksigs ();
  607.     exit (WEXITSTATUS (wstatus));
  608.       }
  609.     }
  610.   return 0;            /* Fool -Wall . . . */
  611. }
  612.  
  613. static char const **diffargv;
  614.  
  615. static void
  616. diffarg (a)
  617.      char const *a;
  618. {
  619.   static unsigned diffargs, diffargsmax;
  620.  
  621.   if (diffargs == diffargsmax)
  622.     {
  623.       if (! diffargsmax)
  624.     {
  625.       diffargv = (char const **) xmalloc (sizeof (char));
  626.       diffargsmax = 8;
  627.     }
  628.       diffargsmax *= 2;
  629.       diffargv = (char const **) realloc (diffargv,
  630.                       diffargsmax * sizeof (char const *));
  631.       if (! diffargv)
  632.     fatal ("out of memory");
  633.     }
  634.   diffargv[diffargs++] = a;
  635. }
  636.  
  637. static void
  638. execdiff (differences_only, option, file1, file2)
  639.      int differences_only;
  640.      char const *option, *file1, *file2;
  641. {
  642.   if (differences_only)
  643.     diffarg ("--suppress-common-lines");
  644.   diffarg (option);
  645.   diffarg ("--");
  646.   diffarg (file1);
  647.   diffarg (file2);
  648.   diffarg (0);
  649.  
  650.   execvp (diffbin, (char **) diffargv);
  651.   write (STDERR_FILENO, diffbin, strlen (diffbin));
  652.   write (STDERR_FILENO, ": not found\n", 12);
  653.   _exit (2);
  654. }
  655.  
  656.  
  657.  
  658.  
  659. /* Signal handling */
  660.  
  661. #define NUM_SIGS (sizeof (sigs) / sizeof (*sigs))
  662. static int const sigs[] = {
  663. #ifdef SIGHUP
  664.        SIGHUP,
  665. #endif
  666. #ifdef SIGQUIT
  667.        SIGQUIT,
  668. #endif
  669. #ifdef SIGTERM
  670.        SIGTERM,
  671. #endif
  672. #ifdef SIGXCPU
  673.        SIGXCPU,
  674. #endif
  675. #ifdef SIGXFSZ
  676.        SIGXFSZ,
  677. #endif
  678.        SIGINT,
  679.        SIGPIPE
  680. };
  681.  
  682. /* Prefer `sigaction' if it is available, since `signal' can lose signals.  */
  683. #if HAVE_SIGACTION
  684. static struct sigaction initial_action[NUM_SIGS];
  685. #define initial_handler(i) (initial_action[i].sa_handler)
  686. #else
  687. static RETSIGTYPE (*initial_action[NUM_SIGS]) ();
  688. #define initial_handler(i) (initial_action[i])
  689. #endif
  690.  
  691. static int volatile ignore_SIGINT;
  692. static int volatile signal_received;
  693. static int sigs_trapped;
  694.  
  695. static RETSIGTYPE
  696. catchsig (s)
  697.      int s;
  698. {
  699. #if ! HAVE_SIGACTION
  700.   signal (s, SIG_IGN);
  701. #endif
  702.   if (! (s == SIGINT && ignore_SIGINT))
  703.     signal_received = s;
  704. }
  705.  
  706. static void
  707. trapsigs ()
  708. {
  709.   int i;
  710.  
  711. #if HAVE_SIGACTION
  712.   struct sigaction catchaction;
  713.   bzero (&catchaction, sizeof (catchaction));
  714.   catchaction.sa_handler = catchsig;
  715. #ifdef SA_INTERRUPT
  716.   /* Non-Posix BSD-style systems like SunOS 4.1.x need this
  717.      so that `read' calls are interrupted properly.  */
  718.   catchaction.sa_flags = SA_INTERRUPT;
  719. #endif
  720.   sigemptyset (&catchaction.sa_mask);
  721.   for (i = 0;  i < NUM_SIGS;  i++)
  722.     sigaddset (&catchaction.sa_mask, sigs[i]);
  723.   for (i = 0;  i < NUM_SIGS;  i++)
  724.     {
  725.       sigaction (sigs[i], 0, &initial_action[i]);
  726.       if (initial_handler (i) != SIG_IGN
  727.       && sigaction (sigs[i], &catchaction, 0) != 0)
  728.     fatal ("signal error");
  729.     }
  730. #else /* ! HAVE_SIGACTION */
  731.   for (i = 0;  i < NUM_SIGS;  i++)
  732.     {
  733.       initial_action[i] = signal (sigs[i], SIG_IGN);
  734.       if (initial_handler (i) != SIG_IGN
  735.       && signal (sigs[i], catchsig) != SIG_IGN)
  736.     fatal ("signal error");
  737.     }
  738. #endif /* ! HAVE_SIGACTION */
  739.   sigs_trapped = 1;
  740. }
  741.  
  742. /* Untrap signal S, or all trapped signals if S is zero.  */
  743. static void
  744. untrapsig (s)
  745.      int s;
  746. {
  747.   int i;
  748.  
  749.   if (sigs_trapped)
  750.     for (i = 0;  i < NUM_SIGS;  i++)
  751.       if ((!s || sigs[i] == s)  &&  initial_handler (i) != SIG_IGN)
  752. #if HAVE_SIGACTION
  753.       sigaction (sigs[i], &initial_action[i], 0);
  754. #else
  755.       signal (sigs[i], initial_action[i]);
  756. #endif
  757. }
  758.  
  759. /* Exit if a signal has been received.  */
  760. static void
  761. checksigs ()
  762. {
  763.   int s = signal_received;
  764.   if (s)
  765.     {
  766.       cleanup ();
  767.  
  768.       /* Yield an exit status indicating that a signal was received.  */
  769.       untrapsig (s);
  770.       kill (getpid (), s);
  771.  
  772.       /* That didn't work, so exit with error status.  */
  773.       exit (2);
  774.     }
  775. }
  776.  
  777.  
  778.  
  779. static void
  780. give_help ()
  781. {
  782.   fprintf (stderr,"l:\tuse the left version\n");
  783.   fprintf (stderr,"r:\tuse the right version\n");
  784.   fprintf (stderr,"e l:\tedit then use the left version\n");
  785.   fprintf (stderr,"e r:\tedit then use the right version\n");
  786.   fprintf (stderr,"e b:\tedit then use the left and right versions concatenated\n");
  787.   fprintf (stderr,"e:\tedit a new version\n");
  788.   fprintf (stderr,"s:\tsilently include common lines\n");
  789.   fprintf (stderr,"v:\tverbosely include common lines\n");
  790.   fprintf (stderr,"q:\tquit\n");
  791. }
  792.  
  793. static int
  794. skip_white ()
  795. {
  796.   int c;
  797.   while (isspace (c = getchar ()) && c != '\n')
  798.     checksigs ();
  799.   if (ferror (stdin))
  800.     perror_fatal ("input error");
  801.   return c;
  802. }
  803.  
  804. static void
  805. flush_line ()
  806. {
  807.   int c;
  808.   while ((c = getchar ()) != '\n' && c != EOF)
  809.     ;
  810.   if (ferror (stdin))
  811.     perror_fatal ("input error");
  812. }
  813.  
  814.  
  815. /* interpret an edit command */
  816. static int
  817. edit (left, lenl, right, lenr, outfile)
  818.      struct line_filter *left;
  819.      int lenl;
  820.      struct line_filter *right;
  821.      int lenr;
  822.      FILE *outfile;
  823. {
  824.   for (;;)
  825.     {
  826.       int cmd0, cmd1;
  827.       int gotcmd = 0;
  828.  
  829.       cmd1 = 0; /* Pacify `gcc -W'.  */
  830.  
  831.       while (!gotcmd)
  832.     {
  833.       if (putchar ('%') != '%')
  834.         perror_fatal ("output error");
  835.       ck_fflush (stdout);
  836.  
  837.       cmd0 = skip_white ();
  838.       switch (cmd0)
  839.         {
  840.         case 'l': case 'r': case 's': case 'v': case 'q':
  841.           if (skip_white () != '\n')
  842.         {
  843.           give_help ();
  844.           flush_line ();
  845.           continue;
  846.         }
  847.           gotcmd = 1;
  848.           break;
  849.  
  850.         case 'e':
  851.           cmd1 = skip_white ();
  852.           switch (cmd1)
  853.         {
  854.         case 'l': case 'r': case 'b':
  855.           if (skip_white () != '\n')
  856.             {
  857.               give_help ();
  858.               flush_line ();
  859.               continue;
  860.             }
  861.           gotcmd = 1;
  862.           break;
  863.         case '\n':
  864.           gotcmd = 1;
  865.           break;
  866.         default:
  867.           give_help ();
  868.           flush_line ();
  869.           continue;
  870.         }
  871.           break;
  872.         case EOF:
  873.           if (feof (stdin))
  874.         {
  875.           gotcmd = 1;
  876.           cmd0 = 'q';
  877.           break;
  878.         }
  879.           /* falls through */
  880.         default:
  881.           flush_line ();
  882.           /* falls through */
  883.         case '\n':
  884.           give_help ();
  885.           continue;
  886.         }
  887.     }
  888.  
  889.       switch (cmd0)
  890.     {
  891.     case 'l':
  892.       lf_copy (left, lenl, outfile);
  893.       lf_skip (right, lenr);
  894.       return 1;
  895.     case 'r':
  896.       lf_copy (right, lenr, outfile);
  897.       lf_skip (left, lenl);
  898.       return 1;
  899.     case 's':
  900.       suppress_common_flag = 1;
  901.       break;
  902.     case 'v':
  903.       suppress_common_flag = 0;
  904.       break;
  905.     case 'q':
  906.       return 0;
  907.     case 'e':
  908.       if (! tmpname && ! (tmpname = private_tempnam (0, "sdiff", 1, 0)))
  909.         perror_fatal ("temporary file name");
  910.  
  911.       tmpmade = 1;
  912.  
  913.       {
  914.         FILE *tmp = ck_fopen (tmpname, "w+");
  915.  
  916.         if (cmd1 == 'l' || cmd1 == 'b')
  917.           lf_copy (left, lenl, tmp);
  918.         else
  919.           lf_skip (left, lenl);
  920.  
  921.         if (cmd1 == 'r' || cmd1 == 'b')
  922.           lf_copy (right, lenr, tmp);
  923.         else
  924.           lf_skip (right, lenr);
  925.  
  926.         ck_fflush (tmp);
  927.  
  928.         {
  929.           pid_t pid;
  930.           int wstatus;
  931.  
  932.           ignore_SIGINT = 1;
  933.           checksigs ();
  934.  
  935.           pid = vfork ();
  936.           if (pid == 0)
  937.         {
  938.           char const *argv[3];
  939.           int i = 0;
  940.  
  941.           argv[i++] = edbin;
  942.           argv[i++] = tmpname;
  943.           argv[i++] = 0;
  944.  
  945.           execvp (edbin, (char **) argv);
  946.           write (STDERR_FILENO, edbin, strlen (edbin));
  947.           write (STDERR_FILENO, ": not found\n", 12);
  948.           _exit (1);
  949.         }
  950.  
  951.           if (pid < 0)
  952.         perror_fatal ("fork failed");
  953.  
  954.           while (waitpid (pid, &wstatus, 0) < 0)
  955.         if (errno == EINTR)
  956.           checksigs ();
  957.         else
  958.           perror_fatal ("wait failed");
  959.  
  960.           ignore_SIGINT = 0;
  961.  
  962.           if (! (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) < 1))
  963.         fatal ("Subsidiary editor failed");
  964.         }
  965.  
  966.         if (fseek (tmp, 0L, SEEK_SET) != 0)
  967.           perror_fatal ("fseek");
  968.         {
  969.           /* SDIFF_BUFSIZE is too big for a local var
  970.          in some compilers, so we allocate it dynamically.  */
  971.           char *buf = xmalloc (SDIFF_BUFSIZE);
  972.           size_t size;
  973.  
  974.           while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
  975.         {
  976.           checksigs ();
  977.           ck_fwrite (buf, size, outfile);
  978.         }
  979.           ck_fclose (tmp);
  980.  
  981.           free (buf);
  982.         }
  983.         return 1;
  984.       }
  985.     default:
  986.       give_help ();
  987.       break;
  988.     }
  989.     }
  990. }
  991.  
  992.  
  993.  
  994. /* Alternately reveal bursts of diff output and handle user commands.  */
  995. static int
  996. interact (diff, left, right, outfile)
  997.      struct line_filter *diff;
  998.      struct line_filter *left;
  999.      struct line_filter *right;
  1000.      FILE *outfile;
  1001. {
  1002.   for (;;)
  1003.     {
  1004.       char diff_help[256];
  1005.       int snarfed = lf_snarf (diff, diff_help, sizeof (diff_help));
  1006.  
  1007.       if (snarfed <= 0)
  1008.     return snarfed;
  1009.  
  1010.       checksigs ();
  1011.  
  1012.       switch (diff_help[0])
  1013.     {
  1014.     case ' ':
  1015.       puts (diff_help + 1);
  1016.       break;
  1017.     case 'i':
  1018.       {
  1019.         int lenl = atoi (diff_help + 1), lenr, lenmax;
  1020.         char *p = strchr (diff_help, ',');
  1021.  
  1022.         if (!p)
  1023.           fatal (diff_help);
  1024.         lenr = atoi (p + 1);
  1025.         lenmax = max (lenl, lenr);
  1026.  
  1027.         if (suppress_common_flag)
  1028.           lf_skip (diff, lenmax);
  1029.         else
  1030.           lf_copy (diff, lenmax, stdout);
  1031.  
  1032.         lf_copy (left, lenl, outfile);
  1033.         lf_skip (right, lenr);
  1034.         break;
  1035.       }
  1036.     case 'c':
  1037.       {
  1038.         int lenl = atoi (diff_help + 1), lenr;
  1039.         char *p = strchr (diff_help, ',');
  1040.  
  1041.         if (!p)
  1042.           fatal (diff_help);
  1043.         lenr = atoi (p + 1);
  1044.         lf_copy (diff, max (lenl, lenr), stdout);
  1045.         if (! edit (left, lenl, right, lenr, outfile))
  1046.           return 0;
  1047.         break;
  1048.       }
  1049.     default:
  1050.       fatal (diff_help);
  1051.       break;
  1052.     }
  1053.     }
  1054. }
  1055.  
  1056.  
  1057.  
  1058. /* temporary lossage: this is torn from gnu libc */
  1059. /* Return nonzero if DIR is an existing directory.  */
  1060. static int
  1061. diraccess (dir)
  1062.      char const *dir;
  1063. {
  1064.   struct stat buf;
  1065.   return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
  1066. }
  1067.  
  1068. /* Return nonzero if FILE exists.  */
  1069. static int
  1070. exists (file)
  1071.      char const *file;
  1072. {
  1073.   struct stat buf;
  1074.   return stat (file, &buf) == 0;
  1075. }
  1076.  
  1077. /* These are the characters used in temporary filenames.  */
  1078. static char const letters[] =
  1079.   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  1080.  
  1081. /* Generate a temporary filename.
  1082.    If DIR_SEARCH is nonzero, DIR and PFX are used as
  1083.    described for tempnam.  If not, a temporary filename
  1084.    in P_tmpdir with no special prefix is generated.  If LENPTR
  1085.    is not 0, *LENPTR is set the to length (including the
  1086.    terminating '\0') of the resultant filename, which is returned.
  1087.    This goes through a cyclic pattern of all possible filenames
  1088.    consisting of five decimal digits of the current pid and three
  1089.    of the characters in `letters'.  Data for tempnam and tmpnam
  1090.    is kept separate, but when tempnam is using P_tmpdir and no
  1091.    prefix (i.e, it is identical to tmpnam), the same data is used.
  1092.    Each potential filename is tested for an already-existing file of
  1093.    the same name, and no name of an existing file will be returned.
  1094.    When the cycle reaches its end (12345ZZZ), 0 is returned.  */
  1095.  
  1096.  
  1097. static char *
  1098. private_tempnam (dir, pfx, dir_search, lenptr)
  1099.      char const *dir;
  1100.      char const *pfx;
  1101.      int dir_search;
  1102.      size_t *lenptr;
  1103. {
  1104.   static char const tmpdir[] = PVT_tmpdir;
  1105.   static struct
  1106.     {
  1107.       char buf[3];
  1108.       char *s;
  1109.       size_t i;
  1110.     } infos[2], *info;
  1111.   static char *buf;
  1112.   static size_t bufsize = 1;
  1113.   static pid_t oldpid = 0;
  1114.   pid_t pid = getpid ();
  1115.   register size_t len, plen;
  1116.  
  1117.   if (dir_search)
  1118.     {
  1119.       register char const *d = getenv ("TMPDIR");
  1120.       if (d && !diraccess (d))
  1121.     d = 0;
  1122.       if (!d && dir && diraccess (dir))
  1123.     d = dir;
  1124.       if (!d && diraccess (tmpdir))
  1125.     d = tmpdir;
  1126.       if (!d && diraccess ("/tmp"))
  1127.     d = "/tmp";
  1128.       if (!d)
  1129.     {
  1130.       errno = ENOENT;
  1131.       return 0;
  1132.     }
  1133.       dir = d;
  1134.     }
  1135.   else
  1136.     dir = tmpdir;
  1137.  
  1138.   if (pfx && *pfx)
  1139.     {
  1140.       plen = strlen (pfx);
  1141.       if (plen > 5)
  1142.     plen = 5;
  1143.     }
  1144.   else
  1145.     plen = 0;
  1146.  
  1147.   if (dir != tmpdir && !strcmp (dir, tmpdir))
  1148.     dir = tmpdir;
  1149.   info = &infos[(plen == 0 && dir == tmpdir) ? 1 : 0];
  1150.  
  1151.   if (pid != oldpid)
  1152.     {
  1153.       oldpid = pid;
  1154.       info->buf[0] = info->buf[1] = info->buf[2] = '0';
  1155.       info->s = &info->buf[0];
  1156.       info->i = 0;
  1157.     }
  1158.  
  1159.   len = strlen (dir) + 1 + plen + 8;
  1160.   if (bufsize <= len)
  1161.     {
  1162.       do
  1163.     {
  1164.       bufsize *= 2;
  1165.     }
  1166.       while (bufsize <= len);
  1167.  
  1168.       if (buf)
  1169.     free (buf);
  1170.       buf = xmalloc (bufsize);
  1171.     }
  1172.   for (;;)
  1173.     {
  1174.       *info->s = letters[info->i];
  1175.       sprintf (buf, "%s/%.*s%.5lu%.3s", dir, (int) plen, pfx,
  1176.            (unsigned long) pid % 100000, info->buf);
  1177.       if (!exists (buf))
  1178.     break;
  1179.       ++info->i;
  1180.       if (info->i > sizeof (letters) - 1)
  1181.     {
  1182.       info->i = 0;
  1183.       if (info->s == &info->buf[2])
  1184.         {
  1185.           errno = EEXIST;
  1186.           return 0;
  1187.         }
  1188.       ++info->s;
  1189.     }
  1190.     }
  1191.  
  1192.   if (lenptr)
  1193.     *lenptr = len;
  1194.   return buf;
  1195. }
  1196.